package http

import (
	"context"
	"fmt"
	ginzap "github.com/gin-contrib/zap"
	"github.com/gin-gonic/gin"
	"github.com/google/wire"
	consulApi "github.com/hashicorp/consul/api"
	"github.com/pkg/errors"
	"github.com/spf13/viper"
	"go.uber.org/zap"
	"net/http"
	"time"
)

// 端口和模式
type Options struct {
	Port int
	Mode string
}

type Server struct {
	o          *Options
	app        string
	host       string
	port       int
	logger     *zap.Logger
	router     *gin.Engine
	httpServer http.Server
	consulCli  *consulApi.Client
}

// 实例化选项
func NewOptions(v *viper.Viper) (*Options, error) {
	var (
		err error
		o   = new(Options)
	)
	if err = v.UnmarshalKey("http", o); err != nil {
		return nil, errors.Wrap(err, "初始化http 选项失败")
	}
	return o, nil
}

type InitControllers func(r *gin.Engine)

// 实例化路由
func NewRouter(o *Options, logger *zap.Logger, init InitControllers) *gin.Engine {
	// 配置gin
	gin.SetMode(o.Mode)
	r := gin.New()

	r.Use(ginzap.Ginzap(logger, time.RFC3339, true))

	// 初始化
	init(r)
	return r
}

// 实例化服务
func New(o *Options, logger *zap.Logger, router *gin.Engine, consulCli *consulApi.Client) (*Server, error) {
	var s = &Server{
		o:         o,
		logger:    logger.With(zap.String("type", "http.Server")),
		router:    router,
		consulCli: consulCli,
	}
	return s, nil
}

func (s *Server) Application(name string) {
	s.app = name
}

// 注册
func (s *Server) register() error {
	addr := fmt.Sprintf("%s:%d", s.host, s.port)
	fmt.Println("addr", addr)
	check := &consulApi.AgentServiceCheck{
		Interval:                       "5s", // 健康检查间隔
		TCP:                            addr,
		DeregisterCriticalServiceAfter: "10s", //check失败后30秒删除本服务，注销时间，相当于过期时间
	}
	id := fmt.Sprintf("%s[%s:%d]", s.app, s.host, s.port)
	// 注册新的服务
	svcReg := &consulApi.AgentServiceRegistration{
		ID:                id,
		Name:              s.app,
		Tags:              []string{"http"},
		Port:              s.port,
		Address:           s.host,
		EnableTagOverride: true,
		Check:             check,
		Checks:            nil,
	}
	if err := s.consulCli.Agent().ServiceRegister(svcReg); err != nil {
		return errors.Wrap(err, "注册服务失败")
	}
	s.logger.Info("register http server success", zap.String("id", id))
	return nil
}

// 删除注册
func (s *Server) deRegister() error {
	id := fmt.Sprintf("%s[%s:%d]", s.app, s.host, s.port)

	if err := s.consulCli.Agent().ServiceDeregister(id); err != nil {
		return errors.Wrapf(err, "删除注册失败 [key=%s]", id)
	}
	s.logger.Info("取消注册http server", zap.String("service", id))
	return nil
}

// 启动服务
func (s *Server) Start() error {
	// 获取端口
	s.port = s.o.Port
	//s.host = netutil.GetLocalIP4()
	s.host = "127.0.0.1"
	addr := fmt.Sprintf("%s:%d", s.host, s.port)
	s.httpServer = http.Server{Addr: addr, Handler: s.router}
	go func() {
		if err := s.httpServer.ListenAndServe(); err != nil {
			s.logger.Fatal("监听服务失败：", zap.Error(err))
			return
		}
	}()
	// 执行注册
	s.register()
	return nil
}

// 停止服务
func (s *Server) Stop() error {
	s.logger.Info("停止服务")
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) // 平滑关闭，等待5秒钟处理
	defer cancel()

	if err := s.deRegister(); err != nil {
		return errors.Wrap(err, "停止服务失败")
	}
	if err := s.httpServer.Shutdown(ctx); err != nil {
		return errors.Wrap(err, "shutdown 服务失败")
	}
	return nil
}

var ProviderSet = wire.NewSet(New, NewRouter, NewOptions)
